//
// Created by vivi on 15/09/2017.
//

#ifndef HAIRCORE_HAIRSELLESIMULATOR_H
#define HAIRCORE_HAIRSELLESIMULATOR_H

#include "HairBaseSimulator.h"
#include "HairReader.h"
#include <vector>

namespace XRwy {

	struct HairSelleSimulatorConfiguration: public HairBaseSimulatorConfiguration {
		float k;
		float damping;
		float air_damping;
		Eigen::Vector3f acc;

		HairSelleSimulatorConfiguration(const HairSimulatorConfDict &d):
				HairBaseSimulatorConfiguration(d) {

			HairSimulatorConfDictHelperUtility u;
			k = u.get_float(d, "k");
			damping = u.get_float(d, "damping");
			air_damping = u.get_float(d, "air_damping");
			acc << u.get_float(d, "acc_x"), u.get_float(d, "acc_y"), u.get_float(d, "acc_z");
		}
	};

	struct HairSelleSimulatorSpring {
		const ParticleView *p1, *p2;
		float k;
		float l0;
		float damping;

		HairSelleSimulatorSpring(const ParticleView *p1_, const ParticleView *p2_, const float k_, const float damping_) {
			const bool _1_smaller_than_2 = p1_->local_id < p2_->local_id;

			this->p1 = _1_smaller_than_2 ? p1_ : p2_;
			this->p2 = _1_smaller_than_2 ? p2_ : p1_;
			this->k = k_;
			this->l0 = (get_pos1() - get_pos2()).norm();
			this->damping = damping_;
		}

		inline const Eigen::Vector3f & get_pos1() const { return *(p1->pos_ptr); }
		inline const Eigen::Vector3f & get_pos2() const { return *(p2->pos_ptr); }

		//p1's impulse
		Eigen::Vector3f impulse(const float t) const {
			Eigen::Vector3f d = get_pos2() - get_pos1();
			const float l = d.norm();
			d /= l;

			return t * k * (l - l0) * d;
		}

		//p1-p1, p2-p2's matrix
		Eigen::Matrix3f matrix(const float t) const {
			Eigen::Vector3f d = get_pos2() - get_pos1();
			float l = d.norm();
			d /= l;

			float f = k / l0 * t * t + damping * t;

			Eigen::Matrix3f dm;
			for (size_t i = 0; i < 3; ++i)
				for (size_t j = 0; j < 3; ++j)
					dm(i, j) = f * d(i) * d(j);

			return std::move(dm);
		}
	};

	class HairSelleSpringSolver {
	public:
		HairSelleSpringSolver(std::vector<HairSelleSimulatorSpring> &springs);
	private:
		std::vector<HairSelleSimulatorSpring> *springs_ptr;

		size_t particle_size; //the max particle-id in springs + 1 because particle-id starts with 0
		size_t dimension_size; //equals to particle_size * 3
	};

	class HairAcceleratedSelleSimulator;

	class HairSelleSimulator: public HairBaseSimulator {
		friend class HairAcceleratedSelleSimulator;
	public:
		HairSelleSimulator(const HairReader & reader, const HairSelleSimulatorConfiguration &conf);
		virtual ~HairSelleSimulator() = default;

		virtual void strand_dynamic(const Eigen::Affine3f &, const float t);

	protected:
		std::vector<Eigen::Vector3f> posv, velv;
		std::vector<HairSelleSimulatorSpring> springs;

		const HairSelleSimulatorConfiguration &get_conf() {
			auto ptr = dynamic_cast<HairSelleSimulatorConfiguration *>(conf_ptr);
			return *ptr;
		}
	};
}

#endif //HAIRCORE_HAIRSELLESIMULATOR_H
